package com.xiaoyu.lanling.app;

import android.app.ActivityManager;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.graphics.Bitmap;

import androidx.emoji.text.EmojiCompat;

import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.memory.MemoryTrimType;
import com.facebook.common.util.ByteConstants;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.qiniu.android.common.AutoZone;
import com.qiniu.android.storage.Configuration;
import com.tencent.bugly.crashreport.CrashReport;
import com.xiaoyu.base.AppConfig;
import com.xiaoyu.base.app.Trimmable;
import com.xiaoyu.base.config.HttpConfig;
import com.xiaoyu.base.data.UserData;
import com.xiaoyu.base.data.UserInfoDataProvider;
import com.xiaoyu.base.log.AppPerformanceLogClient;
import com.xiaoyu.base.net.NetworkErrorProcessor;
import com.xiaoyu.base.net.interceptor.RefreshTokenInterceptor;
import com.xiaoyu.base.net.token.TokenRefresher;
import com.xiaoyu.base.serverpush.ServerEventFactory;
import com.xiaoyu.base.ut.DataTrackProvider;
import com.xiaoyu.base.utils.upload.QiniuUploadManager;
import com.xiaoyu.lanling.BuildConfig;
import com.xiaoyu.lanling.common.config.CPBundledEmojiCompatConfig;
import com.xiaoyu.lanling.common.config.HeaderConfig;
import com.xiaoyu.lanling.data.NotificationData;
import com.xiaoyu.lanling.feature.chat.util.IMInit;
import com.xiaoyu.lanling.feature.login.controller.UserDataLifecycleListenerImpl;
import com.xiaoyu.lanling.feature.serverpush.ServerPushFactoryProxy;
import com.xiaoyu.lanling.feature.voicecall.data.CallData;
import com.xiaoyu.lanling.feature.voicecall.listener.CallLifecycleListener;
import com.xiaoyu.lanling.log.NetworkLogClient;
import com.xiaoyu.lanling.media.image.OkHttpImagePipelineConfigFactory;
import com.xiaoyu.lanling.media.image.fresco.CacheKeyFactory;
import com.xiaoyu.lanling.media.image.fresco.FrescoBitmapMemoryCacheParamsSupplier;
import com.xiaoyu.lanling.net.CustomNetworkErrorProcessor;
import com.xiaoyu.lanling.net.OkHttpDns;
import com.xiaoyu.lanling.net.RequestProxyFactoryImpl;
import com.xiaoyu.lib_av.manager.AgoraManager;
import com.xiaoyu.lib_av.manager.CallManager;
import com.xiaoyu.net.OkHttpClientSupplier;
import com.xiaoyu.net.RequestProvider;
import com.xiaoyu.net.dns.DnsProxy;
import com.xiaoyu.net.intercept.RefreshTokenInterceptorProxy;
import com.xiaoyu.net.log.NetworkLogClientProxy;
import com.xiaoyu.net.queue.RequestQueue;

import frescoextra.FrescoMemoryTrimmableRegistry;
import in.srain.cube.app.init.InitializableAsync;
import in.srain.cube.app.init.InitializableSync;
import in.srain.cube.app.lifecycle.AppActivityLifecycleCallbacks;
import in.srain.cube.request.RequestManager;
import in.srain.cube.util.CLog;
import in.srain.cube.util.Env;
import in.srain.cube.util.UTUtil;
import in.srain.cube.util.log.LogEvent;
import okhttp3.OkHttpClient;

import static com.xiaoyu.base.log.BaseLogClient.KEY_PACKAGE_NAME;

public class InitProcessor implements InitializableSync, InitializableAsync, Trimmable {

    private final static String TAG = "InitProcessor";

    private final Application mApplication;

    private final FrescoMemoryTrimmableRegistry mFrescoMemoryTrimmableRegistry;

    public InitProcessor(Application application) {
        mApplication = application;
        mFrescoMemoryTrimmableRegistry = new FrescoMemoryTrimmableRegistry();
    }

    @Override
    public void initiateSync(Context context) {
        final LogEvent logEvent = new LogEvent("init-sync");
        logEvent.startTimeForKey("total-time");

        logEvent.startTimeForKey("request");
        RefreshTokenInterceptorProxy.setRefreshTokenInterceptor(new RefreshTokenInterceptor());
        DnsProxy.setDns(new OkHttpDns());
        TokenRefresher.setRefreshTokenApi(HttpConfig.USER_REFRESH_TOKEN);
        NetworkErrorProcessor.setCustomNetworkExceptionConsumer(CustomNetworkErrorProcessor.Companion::process);
        ServerEventFactory.setProxy(new ServerPushFactoryProxy());
        RequestManager.getInstance().setRequestProxyFactory(new RequestProxyFactoryImpl());
        NetworkLogClientProxy.Companion.setClient(NetworkLogClient.Companion.getInstance());
        logEvent.endTimeForKey("request");

        mApplication.registerActivityLifecycleCallbacks(AppActivityLifecycleCallbacks.getInstance());

        logEvent.startTimeForKey("ut-util");
        initUTUtil();
        logEvent.endTimeForKey("ut-util");

        logEvent.startTimeForKey("init-request");
        initRequest(context);
        logEvent.endTimeForKey("init-request");

        logEvent.startTimeForKey("init-bugly");
        initBugly(context);
        logEvent.endTimeForKey("init-bugly");

        logEvent.startTimeForKey("init-emoji");
        initEmoji(context);
        logEvent.endTimeForKey("init-emoji");

        logEvent.startTimeForKey("init-userdata");
        UserInfoDataProvider.init(HttpConfig.USER_GET_USER_BASIC_INFO);
        UserData.getInstance().init(new UserDataLifecycleListenerImpl());
        UserData.getInstance().initiateSync(context);
        logEvent.endTimeForKey("init-userdata");

        logEvent.startTimeForKey("init-im");
        initIM();
        logEvent.endTimeForKey("init-im");

        logEvent.endTimeForKey("total-time");
        AppPerformanceLogClient.Companion.getInstance().addLog(logEvent);
    }

    @Override
    public void initiateAsync(Context context) {
        final LogEvent logEvent = new LogEvent("init-async");
        logEvent.startTimeForKey("total-time");

        logEvent.startTimeForKey("init-fresco");
        initFresco(context);
        logEvent.endTimeForKey("init-fresco");

        logEvent.startTimeForKey("init-userdata");
        UserData.getInstance().initiateAsync(context);
        logEvent.endTimeForKey("init-userdata");

        logEvent.startTimeForKey("init-qiniu");
        initQiniu();
        logEvent.endTimeForKey("init-qiniu");

        logEvent.startTimeForKey("init-agora");
        initAgora();
        logEvent.endTimeForKey("init-agora");

        logEvent.startTimeForKey("init-sdk-if-agree-privacy");
        PrivacySDKInitProcessor.initSDKIfAgreePrivacyAsync(context);
        logEvent.endTimeForKey("init-sdk-if-agree-privacy");

        logEvent.startTimeForKey("init-notification-data");
        NotificationData.getInstance().initiateAsync(context);
        logEvent.endTimeForKey("init-notification-data");

        logEvent.endTimeForKey("total-time");
        AppPerformanceLogClient.Companion.getInstance().addLog(logEvent);
    }


    private void initUTUtil() {
        UTUtil.getInstance().init(DataTrackProvider.get());
        UTUtil.getInstance().setSuperProperty("package_name", BuildConfig.APPLICATION_ID);
        LogEvent.putBaseData(KEY_PACKAGE_NAME, BuildConfig.APPLICATION_ID);
    }

    private void initRequest(Context context) {
        RequestQueue.getInstance().init(context);
        final RequestProvider requestProvider = RequestProvider.getInstance();
        requestProvider.setHeader("App-Key", AppConfig.appKey());
        requestProvider.setHeader("Platform", "android");
        requestProvider.setHeader("Version-Code", BuildConfig.VERSION_CODE);
        requestProvider.setHeader("Version-Name", BuildConfig.VERSION_NAME);
        requestProvider.setHeader("App-Channel", UTUtil.getInstance().getChannelId());
        requestProvider.setHeader("User-Agent", HeaderConfig.INSTANCE.getUserAgent());
    }

    private void initFresco(Context context) {
        final OkHttpClient okHttpClient = OkHttpClientSupplier.getResClient();

        DiskCacheConfig smallDiskCacheConfig = DiskCacheConfig.newBuilder(context)
                .setBaseDirectoryPath(context.getApplicationContext().getCacheDir())
                .setBaseDirectoryName("frescoSmallDiskCache")
                .setMaxCacheSize(128 * ByteConstants.MB)
                .setMaxCacheSizeOnLowDiskSpace(64 * ByteConstants.MB)
                .setMaxCacheSizeOnVeryLowDiskSpace(24 * ByteConstants.MB)
                .build();

        DiskCacheConfig mainDiskCacheConfig = DiskCacheConfig.newBuilder(context)
                .setBaseDirectoryPath(context.getApplicationContext().getCacheDir())
                .setBaseDirectoryName("frescoMainDiskCache")
                .setMaxCacheSize(128 * ByteConstants.MB)
                .setMaxCacheSizeOnLowDiskSpace(64 * ByteConstants.MB)
                .setMaxCacheSizeOnVeryLowDiskSpace(24 * ByteConstants.MB)
                .build();

        ImagePipelineConfig config = OkHttpImagePipelineConfigFactory
                .newBuilder(context, okHttpClient)
                .setBitmapsConfig(Bitmap.Config.RGB_565)
                .setCacheKeyFactory(new CacheKeyFactory())
                .setBitmapMemoryCacheParamsSupplier(new FrescoBitmapMemoryCacheParamsSupplier((ActivityManager)
                        context.getSystemService(Context.ACTIVITY_SERVICE)))
                .setSmallImageDiskCacheConfig(smallDiskCacheConfig)
                .setMainDiskCacheConfig(mainDiskCacheConfig)
                .setMemoryTrimmableRegistry(mFrescoMemoryTrimmableRegistry)
                .setResizeAndRotateEnabledForNetwork(true)
                .build();

        Fresco.initialize(context, config);
    }

    private void initEmoji(Context context) {
        EmojiCompat.Config config = new CPBundledEmojiCompatConfig(context);
        EmojiCompat.init(config);
    }

    private void initBugly(Context context) {
        CrashReport.initCrashReport(context, AppConfig.buglyAppId(), Env.isDev());
    }

    private void initQiniu() {
        Configuration config = new Configuration.Builder()
                .connectTimeout(10)
                .useHttps(true)
                .responseTimeout(60)
                .zone(AutoZone.autoZone)
                .dns(null)
                .build();
        QiniuUploadManager.Companion.getInstance().init(config);
    }

    /**
     * Must init NIM observers in main thread, all observers are called in main thread.
     */
    private void initIM() {
        IMInit.init();
    }

    private void initAgora() {
        CallManager.Companion.getInstance().init(CallLifecycleListener.getInstance());
        AgoraManager.Companion.getInstance().login(CallData.sLoginRunnable);
    }

    @Override
    public void onTrimMemory(int level) {
        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
                CLog.v(TAG, "onTrimMemory() OnAppBackgrounded - level=%s", level);
                mFrescoMemoryTrimmableRegistry.trim(MemoryTrimType.OnAppBackgrounded);
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
                CLog.v(TAG, "onTrimMemory() OnCloseToDalvikHeapLimit - level=%s", level);
                mFrescoMemoryTrimmableRegistry.trim(MemoryTrimType.OnCloseToDalvikHeapLimit);
                break;
            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                CLog.v(TAG, "onTrimMemory() OnSystemLowMemoryWhileAppInForeground - level=%s", level);
                mFrescoMemoryTrimmableRegistry.trim(MemoryTrimType.OnSystemLowMemoryWhileAppInForeground);
                break;
            default:
                CLog.v(TAG, "onTrimMemory() default - level=%s", level);
                break;
        }
    }
}

